home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 11 - 1995 / 11.10 Oct 95 / SlimApp / SampleApp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-03-17  |  17.5 KB  |  664 lines  |  [TEXT/MMCC]

  1. /*==========================================================================
  2.  
  3.                                 SampleApp.c
  4.                                 
  5.     This simple application illustrates the code stripping functionality
  6.     provided by SlimApp.
  7.     
  8.     It provides an "About SampleApp..." menu command to get at a dialog
  9.     box that provides access to the code stripping features, and a simple
  10.     command that counts to 10,000,000 to give you a feel for whether you're
  11.     running native-68K, 68K-emulated, or PPC-native.
  12.  
  13.     Copyright 1995 by Blake Ward.
  14.  
  15.     Permission is granted for unrestricted use, provided the author
  16.     is credited.
  17.         
  18.   ==========================================================================*/
  19.  
  20. #include "SlimApp.h"
  21.  
  22. #include <Palettes.h>
  23.  
  24.  
  25.  
  26. /*============================================================================
  27.   MISC STRUCTURES & CONSTANTS
  28.   ============================================================================*/
  29.  
  30.  
  31. // Menu Resource IDs:
  32. #define kMenuBarID        128
  33.  
  34. #define kAppleMenuID    128
  35. #define kAboutSampleApp    1
  36.  
  37. #define kFileMenuID        129
  38. #define kCount            1
  39. #define kQuit            3
  40.  
  41.  
  42. // Simple dialog for displaying how long it took to count
  43. #define kCountMessageDlg    2000
  44.  
  45.  
  46. // Our sample About Box
  47. #define kAboutBoxDialogID    1000
  48. #define OK                    1
  49. #define ABOUT_OK_OUTLINE    2
  50. #define ABOUT_APP_TITLE        3
  51. #define ABOUT_COPYRIGHT        4
  52. #define ABOUT_VERSION        5
  53. #define REDUCE_CODE_BTN        6
  54. #define ABOUT_CODE_TYPE        7
  55.  
  56. // Dialog for warning the user before stripping the application
  57. #define kWarningAlertID        2001
  58.  
  59.  
  60. // SampleApp's string resources
  61. #define kSampleAppStrings    1000
  62. #define kVersionLabel        1
  63.  
  64.  
  65. /*============================================================================
  66.   INTERNAL PROTOTYPES (Functions used only within this file)
  67.   ============================================================================*/
  68.  
  69. void main(void);
  70. void EventLoop(void);
  71. OSErr Initialize(void);
  72. Boolean DoMenu(long int menuChoice);
  73.  
  74. void DoAboutSampleApp(void);
  75. void DoCountTenMillion(void);
  76.  
  77.  
  78. // User Item drawing routines for the About Box
  79. pascal void DrawStyledVersionString(DialogPtr dp, short itemNumber);
  80. pascal void DrawStyledCodeType(DialogPtr dp, short itemNumber);
  81.  
  82. // Miscellaneous routines useful for dealing with dialog items
  83. pascal void DrawDefaultButtonOutline(DialogPtr dp, short outlineUserItemNumber);
  84. void SetUserItem(DialogPtr dp, short int itemNumber, UserItemUPP drawRoutine);
  85. void EnableDialogControl(DialogPtr dp, short itemNumber, Boolean enable);
  86. void RenameDialogControl(DialogPtr dp, short itemNumber, StringPtr newName);
  87.  
  88.  
  89.  
  90.  
  91. /*============================================================================
  92.   main:
  93.  
  94.       Initializes the toolbox, installs the menus and then calls a loop to
  95.       handle all of the events until the user quits.
  96.  
  97.   ============================================================================*/
  98. void
  99. main(void)
  100. {
  101.     
  102.     if (Initialize() != noErr)
  103.         return;
  104.                 
  105.     // Handle events until they quit
  106.     EventLoop();
  107.  
  108. }
  109.  
  110.  
  111.  
  112. /*============================================================================
  113.   Initialize:
  114.   
  115.       This function initializes the toolbox and sets up the menus.
  116.       
  117.   ============================================================================*/
  118. OSErr
  119. Initialize(void)
  120. {
  121.  
  122.     Handle menuBar;
  123.  
  124.     // Call this once before anything else to ensure we've got a fully expanded
  125.     // heap zone.
  126.     MaxApplZone();
  127.  
  128.     // We don't use much memory, so probably don't need to call this at all...
  129.     MoreMasters();
  130.  
  131.     // Initialize all of the toolbox managers
  132.     InitGraf(&qd.thePort);
  133.     InitFonts();
  134.     InitWindows();
  135.     InitMenus();
  136.     TEInit();
  137.     InitDialogs(nil);
  138.     InitCursor();
  139.  
  140.     // Install our menus (stored in SampleApp.rsrc)
  141.     menuBar = GetNewMBar(kMenuBarID);
  142.     if (menuBar == nil)
  143.         return resNotFound;
  144.  
  145.     SetMenuBar(menuBar);
  146.     
  147.     // Add the DA names to the apple menu
  148.     AppendResMenu(GetMenuHandle(kAppleMenuID), 'DRVR');    /* add DA names to Apple menu */
  149.  
  150.     DrawMenuBar();
  151.  
  152.     return noErr;
  153. }
  154.  
  155.  
  156.  
  157.  
  158. /*============================================================================
  159.   EventLoop:
  160.   
  161.       This function just loops endlessly processing events until the user
  162.       selects Quit from the File menu.
  163.       
  164.       Since SampleApp is an extremely limited application, we don't need  to
  165.       handle very many events here (essentially all we need to watch for is
  166.       menu commands).
  167.       
  168.   ============================================================================*/
  169. void
  170. EventLoop(void)
  171. {
  172.     EventRecord    event;
  173.     Boolean userQuit = false;
  174.     WindowPtr whichWindow;
  175.     Point where;
  176.     short int windowCode;
  177.  
  178.     // Clear out left over events
  179.     FlushEvents(everyEvent, 0);
  180.  
  181.     // Loop until the user 'Quit's
  182.     while (!userQuit) {
  183.  
  184.         // Get the next event
  185.         WaitNextEvent(everyEvent, &event, 30, 0L);
  186.         switch(event.what) {
  187.             case mouseDown:                // In which window, and where??
  188.                 windowCode = FindWindow(event.where, &whichWindow);
  189.                 switch(windowCode) {
  190.                     case inSysWindow:    // It's not in our windows or menus
  191.                         SystemClick(&event, whichWindow);
  192.                         break;
  193.                     case inMenuBar:        // In a menu, handle the command
  194.                         userQuit = DoMenu(MenuSelect(event.where));
  195.                         break;
  196.                     }
  197.                 break; // end mouseDown
  198.             case keyDown:
  199.             case autoKey:                // If a Command key was pressed, pass to MenuKey
  200.                 if((event.modifiers & cmdKey) != 0) {
  201.                     userQuit = DoMenu(MenuKey((char) (event.message & 0xff)));
  202.                     }
  203.                    break;
  204.             case diskEvt:                // Handle disk insertion event
  205.                 if (HiWord(event.message) != noErr) {
  206.                     DILoad();
  207.                     where.h = 112;
  208.                     where.v = 80;
  209.                     DIBadMount(where, event.message);
  210.                     DIUnload();
  211.                     }
  212.                 break;
  213.             }    // switch
  214.         }    // while (!userQuit)
  215. }
  216.  
  217.  
  218.  
  219.  
  220. /*============================================================================
  221.   DoMenu:
  222.  
  223.     This function is called when the user has pulled down a menu or typed the
  224.     command key equivalent of a menu item.
  225.  
  226.   ============================================================================*/
  227. Boolean
  228. DoMenu(long int menuResult)
  229. {
  230.     short int menuID, menuItem;
  231.     unsigned char name[255];
  232.     
  233.     // Find out which menu they selected an item from
  234.     menuID = HiWord(menuResult);
  235.  
  236.     // If it's zero, then they moved the mouse off the menus without
  237.     // selecting anything
  238.     if (menuID == 0) {
  239.         // This seems to be necessary if they're running "Apple Menu Options" and
  240.         // they select a control panel from the hierarchical menu.
  241.         HiliteMenu(0);
  242.         return false;
  243.         }
  244.     
  245.     // Which item in that menu did they select?
  246.     menuItem = LoWord(menuResult);
  247.  
  248.     switch (menuID) {
  249.         case kAppleMenuID:
  250.             if (menuItem == kAboutSampleApp)
  251.                 DoAboutSampleApp();
  252.             else {
  253.                 GetMenuItemText(GetMenuHandle(kAppleMenuID), menuItem, name);
  254.                 OpenDeskAcc(name);
  255.                 }
  256.             break;
  257.         case kFileMenuID:
  258.             switch(menuItem) {
  259.                 case kCount:
  260.                     DoCountTenMillion();
  261.                     break;
  262.                 case kQuit:
  263.                     return true;
  264.                 }
  265.             break;
  266.         }
  267.  
  268.     HiliteMenu(0);
  269.     
  270.     return false;
  271. }
  272.  
  273.  
  274.  
  275.  
  276. /*============================================================================
  277.   DoCountTenMillion:
  278.  
  279.     This simple routine just counts to 10,000,000 and then puts up a quick
  280.     alert box telling the user how long it took.
  281.  
  282.   ============================================================================*/
  283. void
  284. DoCountTenMillion(void)
  285. {
  286.     long int count;
  287.     unsigned long int tm;
  288.     Str63 time;
  289.     CursHandle cursH;
  290.     
  291.     // Change the cursor to a watch while we count
  292.     cursH = GetCursor(watchCursor);
  293.     HLock((Handle)cursH);
  294.     SetCursor(*cursH);
  295.     
  296.     tm = TickCount();
  297.     
  298.     for (count = 0; count < 10000000; count++)
  299.         ;
  300.         
  301.     NumToString(TickCount() - tm, time);
  302.  
  303. #if powerc
  304.     ParamText(time, "\pPowerPC", "\p", "\p");
  305. #else
  306.     ParamText(time, "\p680x0", "\p", "\p");
  307. #endif
  308.  
  309.     SetCursor(&qd.arrow);
  310.     ReleaseResource((Handle)cursH);
  311.  
  312.     // Put up the alert
  313.     NoteAlert(kCountMessageDlg, 0L);
  314.  
  315. }
  316.  
  317.  
  318.  
  319.  
  320. /*============================================================================
  321.   DoAboutSampleApp:
  322.  
  323.     The fat application stripping code is accessed from the About Box.
  324.     This routine displays the about box dialog and handles clicking the
  325.     "Remove Code" and "OK" buttons.
  326.  
  327.     Several things are illustrated in this dialog:
  328.     
  329.       - We dynamically set the label in the button to reflect what we're
  330.         actually going to do.  This makes the button a little big, but it's
  331.         important since the average user is going to have trouble
  332.         understanding the whole "fat application" concept anyway.
  333.       - Once the unneeded code has been stripped, we hide the button since
  334.         they can't do it again and they can't undo it.
  335.       - We always display a message in the dialog indicating which version of
  336.         the application they have.  It's easy to forget after a few months what
  337.         was done to the application, and this provides an easy reminder of which
  338.         version they have.  If you get calls to your customer support department,
  339.         they can easily ask the user to bring up the About Box for a quick check
  340.         of the application's version number and whether it's been stripped.
  341.         
  342.   ============================================================================*/
  343. void
  344. DoAboutSampleApp(void)
  345. {
  346.     short int whichVersion;
  347.     short int item;
  348.     DialogPtr dp;
  349.     UserItemUPP versionStringUPP, codeTypeUPP, outlineDefaultUPP;
  350.     Str255 tmpStr;
  351.  
  352.     dp = GetNewDialog(kAboutBoxDialogID, 0L, (WindowPtr)-1L );
  353.  
  354.     // Set up the drawing routines for the version # and code type user items
  355.     // To make the dialog look a little nicer, we display these using
  356.     // styled text.
  357.     versionStringUPP = NewUserItemProc(DrawStyledVersionString);
  358.     SetUserItem(dp, ABOUT_VERSION, versionStringUPP);
  359.     codeTypeUPP = NewUserItemProc(DrawStyledCodeType);
  360.     SetUserItem(dp, ABOUT_CODE_TYPE, codeTypeUPP);
  361.  
  362.     // Install a user item to draw the outline around the OK button.
  363.     outlineDefaultUPP = NewUserItemProc(DrawDefaultButtonOutline);
  364.     SetUserItem(dp, ABOUT_OK_OUTLINE, outlineDefaultUPP);
  365.  
  366.     // Figure out whether we still have the 68K and PowerPC code
  367.     whichVersion = Has68KPowerPCCode();
  368.  
  369.     // Set up the reduce application button:
  370.     //     - if this version isn't fat, then we can't strip anything out of the binary
  371.     //     - we want the button label to give an indication of what is about to be done
  372.     if (!SafeToStrip())
  373.         EnableDialogControl(dp, REDUCE_CODE_BTN, false);
  374.     if (whichVersion != kFatBinaryApplication)
  375.         HideDialogItem(dp, REDUCE_CODE_BTN);
  376.     else {
  377.         #ifdef powerc
  378.         GetIndString(tmpStr, kSlimAppStrings, kStrip68KButtonLabel);
  379.         #else
  380.         GetIndString(tmpStr, kSlimAppStrings, kStripPowerPCButtonLabel);
  381.         #endif
  382.         RenameDialogControl(dp, REDUCE_CODE_BTN, tmpStr);
  383.         }
  384.     
  385.     // Loop until they click OK, and then tear everything down and return
  386.     while (true) {
  387.         ModalDialog((ModalFilterUPP)0L, &item);
  388.         switch (item) {
  389.             case OK:
  390.                 DisposeDialog(dp);
  391.                 DisposeRoutineDescriptor(versionStringUPP);
  392.                 DisposeRoutineDescriptor(codeTypeUPP);
  393.                 DisposeRoutineDescriptor(outlineDefaultUPP);
  394.                 return;
  395.             case REDUCE_CODE_BTN:
  396.                 #ifdef powerc
  397.                 GetIndString(tmpStr, kSlimAppStrings, kPowerMacOnlyWarning);
  398.                 #else
  399.                 GetIndString(tmpStr, kSlimAppStrings, k68KOnlyWarning);
  400.                 #endif
  401.                 ParamText(tmpStr, "\p", "\p", "\p");
  402.                 if (CautionAlert(kWarningAlertID, 0L) == 1) {
  403.                     SetPort(dp);
  404.                     if (StripFatApplication() == noErr) {
  405.                         HideDialogItem(dp, REDUCE_CODE_BTN);
  406.                         }
  407.                     else StopAlert(kCodeStripFailedErrorDialog, 0L);
  408.                     }
  409.                 // The application version message may have changed, so
  410.                 // just make sure that it completely redraws
  411.                 SetPort(dp);
  412.                 InvalRect(&dp->portRect);
  413.                 break;
  414.             }
  415.         }
  416.  
  417. }
  418.  
  419.  
  420.  
  421.  
  422. /*============================================================================
  423.   DrawStyledVersionString:
  424.  
  425.     This user item just retrieves the short version string from the
  426.     application's resources and displays it centered in small bold geneva.
  427.  
  428.   ============================================================================*/
  429. pascal void
  430. DrawStyledVersionString(DialogPtr dp, short itemNumber)
  431. {
  432.     short itemType;
  433.     Handle item;
  434.     Rect itemRect;
  435.     Style txFace;
  436.     short int txSize;
  437.     short int txFont;
  438.     Str63 versionLabel, versionStr;
  439.     
  440.     // Make sure that we're actually drawing into the dialog
  441.     SetPort(dp);
  442.     
  443.     // Get all of the current values so that we can put them back when we're finished
  444.     txFont = dp->txFont;
  445.     txFace = dp->txFace;
  446.     txSize = dp->txSize;
  447.     
  448.     // Set it up the way we want
  449.     TextFace(bold);
  450.     TextFont(geneva);
  451.     TextSize(9);
  452.     
  453.     // Get the "Version" string and append the current short version
  454.     // string from the 'vers' resource
  455.     GetIndString(versionLabel, kSampleAppStrings, kVersionLabel);
  456.     GetShortVersion(versionStr);
  457.     BlockMoveData(&versionStr[1], &versionLabel[versionLabel[0] + 1], versionStr[0]);
  458.     versionLabel[0] += versionStr[0];
  459.  
  460.     // Get the item rectangle so we know where to draw
  461.     GetDialogItem(dp, itemNumber, &itemType, &item, &itemRect);
  462.  
  463.     // Draw the string
  464.     TextBox(&versionLabel[1], versionLabel[0], &itemRect, teCenter);
  465.  
  466.     // Put the port back the way it was
  467.     TextFace(txFace);
  468.     TextFont(txFont);
  469.     TextSize(txSize);
  470.  
  471. }
  472.  
  473.  
  474.  
  475.  
  476. /*============================================================================
  477.   DrawStyledCodeType:
  478.  
  479.     This user item just checks to see which version of the application we
  480.     currently have and then displays the appropriate message in
  481.     small bold geneva.  If the button is visible (we have a fat version),
  482.     then the message is centered under the button.  Otherwise we draw it
  483.     flush left in the corner of the dialog.
  484.  
  485.   ============================================================================*/
  486. pascal void
  487. DrawStyledCodeType(DialogPtr dp, short itemNumber)
  488. {
  489.     short itemType;
  490.     Handle item;
  491.     Rect itemRect;
  492.     Style txFace;
  493.     short int txSize;
  494.     short int txFont;
  495.     short whichVersion;
  496.     Str255 codeTypeStr;
  497.     
  498.     // Make sure that we're actually drawing into the dialog
  499.     SetPort(dp);
  500.  
  501.     // Get all of the current values so that we can put them back when we're finished
  502.     txFont = dp->txFont;
  503.     txFace = dp->txFace;
  504.     txSize = dp->txSize;
  505.     
  506.     // Set it up the way we want
  507.     TextFace(bold);
  508.     TextFont(geneva);
  509.     TextSize(9);
  510.  
  511.     // Figure out whether we still have the 68K and PowerPC code
  512.     whichVersion = Has68KPowerPCCode();
  513.     
  514.     // Build a version string to display based on the version resource
  515.     GetIndString(codeTypeStr, kSlimAppStrings, whichVersion == k68KApplication ? k68KLabel :
  516.                         (whichVersion == kPowerPCApplication ? kPowerPCLabel : kFatBinaryLabel));
  517.  
  518.     // Get the item rectangle so we know where to draw
  519.     GetDialogItem(dp, itemNumber, &itemType, &item, &itemRect);
  520.  
  521.     // Draw the string
  522.     TextBox(&codeTypeStr[1], codeTypeStr[0], &itemRect,
  523.                 whichVersion == kFatBinaryApplication ? teCenter : teFlushLeft);
  524.  
  525.     // Put the port back the way it was
  526.     TextFace(txFace);
  527.     TextFont(txFont);
  528.     TextSize(txSize);
  529.  
  530. }
  531.  
  532.  
  533.  
  534.  
  535. /*============================================================================        
  536.   DrawDefaultButtonOutline:
  537.  
  538.     This procedure should be installed as the draw routine for a user item
  539.     that overlaps the default button. It assumes that the default item is the
  540.     item number that dialog has stored as the default (normally item #1).
  541.  
  542.     If the item is disabled, the outline is drawn in the gray color or a
  543.     gray pattern.
  544.  
  545.     This routine is overkill for SampleApp, but you might find it useful
  546.     elsewhere.
  547.         
  548.   ============================================================================*/
  549. pascal void
  550. DrawDefaultButtonOutline(DialogPtr dp, short /*outlineUserItemNumber*/)
  551. {
  552.     short itemType;
  553.     Handle item;
  554.     Rect itemRect;
  555.     PenState savePen;
  556.     Boolean grayAvailable = false;
  557.     RGBColor fore, back, gray;
  558.  
  559.     // Make sure we're actually drawing in the dialog
  560.     SetPort(dp);
  561.  
  562.     // Save the pen state so that we can put it back when we're done
  563.     GetPenState(&savePen);
  564.  
  565.     // Use whichever item the dialog thinks is the default
  566.     GetDialogItem(dp, ((DialogPeek)dp)->aDefItem, &itemType, &item, &itemRect);
  567.  
  568.     // We want to draw a thick outline, so the easiest way is to just change
  569.     //the pen thickness
  570.     PenNormal();
  571.     PenSize(3, 3);
  572.     InsetRect(&itemRect, -4, -4);
  573.     
  574.     // Is the button currently enabled?
  575.     if ((*(ControlHandle)item)->contrlHilite == 255) {
  576.         GetForeColor(&fore);
  577.         gray = fore;
  578.         GetBackColor(&back);
  579.         grayAvailable = GetGray(GetGDevice(), &back, &gray);
  580.         if (grayAvailable) {
  581.             RGBForeColor(&gray);
  582.             }
  583.         else PenPat(&qd.dkGray);
  584.         }
  585.  
  586.     FrameRoundRect(&itemRect, 16, 16);
  587.  
  588.     // Put back the original color and pen state
  589.     if (grayAvailable)
  590.         RGBForeColor(&fore);
  591.     SetPenState(&savePen);
  592.  
  593. }
  594.  
  595.  
  596.  
  597.  
  598. /*============================================================================        
  599.   SetUserItem:
  600.  
  601.     Convenience routine for dealing with dialog box items.
  602.     Provides a handy way to associate a UserItemUPP with a user item.
  603.  
  604.   ============================================================================*/
  605. void SetUserItem(DialogPtr dp, short int itemNumber, UserItemUPP drawRoutine)
  606. {
  607.     short itemType;
  608.     Handle item;
  609.     Rect itemRect;
  610.  
  611.     GetDialogItem(dp, itemNumber, &itemType, &item, &itemRect);
  612.     SetDialogItem(dp, itemNumber, itemType, (Handle)drawRoutine, &itemRect);
  613.  
  614. }
  615.  
  616.  
  617.  
  618.  
  619. /*============================================================================        
  620.   EnableDialogItem:
  621.  
  622.     Convenience routine for dealing with dialog box items.
  623.     Just enables/disables the given dialog control item.
  624.  
  625.   ============================================================================*/
  626. void EnableDialogControl(DialogPtr dp, short itemNumber, Boolean enableFlag)
  627. {
  628.     short itemType;
  629.     Handle item;
  630.     Rect itemRect;
  631.  
  632.     GetDialogItem(dp, itemNumber, &itemType, &item, &itemRect);
  633.  
  634.     HiliteControl((ControlHandle)item, enableFlag ? 0 : 255);
  635.  
  636. }
  637.  
  638.  
  639.  
  640. /*============================================================================        
  641.   RenameDialogControl:
  642.  
  643.     Convenience routine for dealing with dialog box items.
  644.     Just changes the title of the given dialog control (probably a button).
  645.  
  646.   ============================================================================*/
  647. void
  648. RenameDialogControl(DialogPtr dp, short itemNumber, StringPtr newName)
  649. {
  650.     short itemType;
  651.     Handle item;
  652.     Rect itemRect;
  653.  
  654.     GetDialogItem(dp, itemNumber, &itemType, &item, &itemRect);
  655.  
  656.     SetControlTitle((ControlHandle)item, newName);
  657.  
  658. }
  659.  
  660.  
  661.  
  662.  
  663.  
  664.